import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import pandas as pd
import sqlite3
import json
import os
from asociety.personality.cfa_utilsO import perform_cfa
from studio.collapsible_help_panel import CollapsibleHelpPanel
from studio.progress_dialog import ProgressManager
from studio.analysis_panel_utils import show_analysis_window
from asociety.generator.llm_engine import llm

UI_TEXT = {
    'zh': {
        'select_db': "选择数据库文件...",
        'run_analysis': "运行验证性因素分析 (CFA)",
        'analysis_results': "模型拟合指数",
        'cfa_title': "验证性因素分析 (CFA)",
        'file_dialog_title': "选择数据库文件"
    },
    'en': {
        'select_db': "Select Database File...",
        'run_analysis': "Run Confirmatory Factor Analysis (CFA)",
        'analysis_results': "Model Fit Indices",
        'cfa_title': "Confirmatory Factor Analysis (CFA)",
        'file_dialog_title': "Select a database file"
    }
}

HELP_CONTENT = {
    'zh': {
        'title': "验证性因素分析 (CFA)",
        'content': """
### 功能与用途
验证性因素分析 (CFA) 是一种统计方法，用于**检验**一个预先存在的理论模型是否与观测数据相拟合。与探索性因素分析（EFA）不同，CFA不探索数据的潜在结构，而是**验证**一个已知的结构。

在这里，我们用CFA来检验由大型语言模型（LLM）生成的性格问卷回答，是否符合“大五人格”理论所预设的、由五个相互独立的因素（神经质、外向性、开放性、宜人性、尽责性）构成的结构。

### 如何使用
1.  点击“选择数据库文件...”并选择一个包含 `personality` 表的 `.db` 文件。
2.  点击“运行验证性因素分析 (CFA)”。

### 如何解读结果
结果是一系列**模型拟合指数**，它们从不同角度评估了理论模型与数据的匹配程度。
-   **CFI (Comparative Fit Index)**: 比较拟合指数。范围在0-1之间，**值 > 0.90 (理想 > 0.95)** 通常被认为是可接受的拟合。
-   **TLI (Tucker-Lewis Index)**: 同样是比较拟合指数，也考虑了模型的简洁性。**值 > 0.90 (理想 > 0.95)** 表示拟合良好。
-   **RMSEA (Root Mean Square Error of Approximation)**: 近似误差均方根。它衡量了模型在多大程度上未能解释数据。**值 < 0.08 (理想 < 0.06)** 表示合理的拟合。
-   **SRMR (Standardized Root Mean Square Residual)**: 标准化残差均方根。基于协方差残差。**值 < 0.08** 表示良好的拟合。
-   **Chi-Square (χ²)**: 卡方检验。在样本量很大时，该检验几乎总是显著的（p < 0.05），因此通常不作为模型接受或拒绝的唯一标准，而是与其他指数结合来看。

**理想的模型拟合结果是：CFI/TLI > 0.95, RMSEA < 0.06, SRMR < 0.08。
"""
    },
    'en': {
        'title': "Confirmatory Factor Analysis (CFA)",
        'content': """
### Function and Purpose
Confirmatory Factor Analysis (CFA) is a statistical method used to **test** how well a pre-existing theoretical model fits the observed data. Unlike Exploratory Factor Analysis (EFA), CFA does not explore the underlying structure of the data but rather **confirms** a known structure.

Here, we use CFA to test whether the personality questionnaire responses generated by a Large Language Model (LLM) conform to the structure presupposed by the "Big Five" personality theory, which consists of five independent factors (Neuroticism, Extraversion, Openness, Agreeableness, and Conscientiousness).

### How to Use
1.  Click "Select Database File..." and choose a `.db` file containing a `personality` table.
2.  Click "Run Confirmatory Factor Analysis (CFA)".

### How to Interpret the Results
The results are a series of **model fit indices**, which evaluate how well the theoretical model matches the data from different perspectives.
-   **CFI (Comparative Fit Index)**: Ranges from 0-1. A **value > 0.90 (ideally > 0.95)** is generally considered an acceptable fit.
-   **TLI (Tucker-Lewis Index)**: Similar to CFI but also accounts for model parsimony. A **value > 0.90 (ideally > 0.95)** indicates a good fit.
-   **RMSEA (Root Mean Square Error of Approximation)**: Measures how well the model fails to explain the data. A **value < 0.08 (ideally < 0.06)** indicates a reasonable fit.
-   **SRMR (Standardized Root Mean Square Residual)**: Based on the covariance residuals. A **value < 0.08** indicates a good fit.
-   **Chi-Square (χ²)**: With large sample sizes, this test is almost always significant (p < 0.05) and is therefore often not the sole criterion for model acceptance or rejection but is considered alongside other indices.

**An ideal model fit would be: CFI/TLI > 0.95, RMSEA < 0.06, SRMR < 0.08.
"""
    }
}


class CFAPanel:
    def __init__(self, parent):
        self.main = ttk.PanedWindow(parent, orient=tk.HORIZONTAL)
        
        main_content_frame = ttk.Frame(self.main)
        self.main.add(main_content_frame, weight=3)

        self.db_path = tk.StringVar()
        self.current_lang = 'zh'
        self.fit_measures = None

        self._create_widgets(main_content_frame)

        help_config = {"title": HELP_CONTENT[self.current_lang]['title'], "content": HELP_CONTENT[self.current_lang]['content']}
        self.collapsible_help = CollapsibleHelpPanel(self.main, help_config, weight=1)

    def _create_widgets(self, parent):
        control_frame = ttk.Frame(parent)
        control_frame.pack(fill='x', padx=10, pady=10)

        self.select_button = ttk.Button(control_frame, text=UI_TEXT[self.current_lang]['select_db'], command=self.select_db_file)
        self.select_button.pack(side='left', padx=(0, 5))
        
        self.db_label = ttk.Label(control_frame, textvariable=self.db_path)
        self.db_label.pack(side='left', fill='x', expand=True)

        self.run_button = ttk.Button(control_frame, text=UI_TEXT[self.current_lang]['run_analysis'], command=self.start_analysis, state=tk.DISABLED)
        self.run_button.pack(side='left', padx=(5, 0))

        self.qwen_button = ttk.Button(control_frame, text="结果解析 (AI)", command=self.start_qwen_analysis, state=tk.DISABLED)
        self.qwen_button.pack(side='left', padx=(5, 0))

        self.copy_button = ttk.Button(control_frame, text="数据拷贝 (JSON)", command=self.copy_analysis_data_to_clipboard, state=tk.DISABLED)
        self.copy_button.pack(side='left', padx=(5, 0))

        self.progress_manager = ProgressManager(self.main)
        
        results_frame = ttk.LabelFrame(parent, text=UI_TEXT[self.current_lang]['analysis_results'])
        results_frame.pack(fill='both', expand=True, padx=10, pady=10)
        
        self.results_tree = ttk.Treeview(results_frame, columns=('Index', 'Value'), show='headings')
        self.results_tree.heading('Index', text='Fit Index')
        self.results_tree.heading('Value', text='Value')
        self.results_tree.pack(fill='both', expand=True)

    def select_db_file(self):
        filepath = filedialog.askopenfilename(
            title=UI_TEXT[self.current_lang]['file_dialog_title'],
            filetypes=(("Database files", "*.db"), ("All files", "*.*")) ,
            initialdir='data/db/backup'
        )
        if filepath:
            self.db_path.set(filepath)
            self.run_button.config(state=tk.NORMAL)
            self._clear_results()

    def start_analysis(self):
        db_path = self.db_path.get()
        if not db_path:
            return

        def analysis_task(progress_dialog):
            progress_dialog.update_message("Performing CFA...")
            fit_measures = perform_cfa(db_path)
            return fit_measures

        def on_success(result):
            if result is not None:
                self.fit_measures = result
                self._display_results(self.fit_measures)
                self.qwen_button.config(state=tk.NORMAL)
                self.copy_button.config(state=tk.NORMAL)

        def on_error(error):
            messagebox.showerror("Analysis Error", f"An error occurred during CFA: {error}")

        self._clear_results()
        self.progress_manager.run_with_progress(
            analysis_task,
            title="CFA in Progress",
            message="Loading data and building model...",
            success_callback=on_success,
            error_callback=on_error
        )

    def _display_results(self, fit_measures):
        # Clear only the UI tree, not the underlying data
        for i in self.results_tree.get_children():
            self.results_tree.delete(i)
        
        for index, value in fit_measures.items():
            self.results_tree.insert('', 'end', values=(index, f"{value:.4f}"))

    def _clear_results(self):
        # Clear UI
        for i in self.results_tree.get_children():
            self.results_tree.delete(i)
        # Clear data
        self.fit_measures = None
        # Reset button states
        self.qwen_button.config(state=tk.DISABLED)
        self.copy_button.config(state=tk.DISABLED)

    def copy_analysis_data_to_clipboard(self):
        if not self.fit_measures:
            messagebox.showwarning("No Data", "There is no analysis data to copy. Please run the analysis first.")
            return

        try:
            # Convert numpy types to native Python types for JSON serialization
            native_fit_measures = {k: (float(v) if hasattr(v, 'item') else v) for k, v in self.fit_measures.items()}

            data_to_export = {
                "source_file": os.path.basename(self.db_path.get()),
                "analysis_type": "Confirmatory Factor Analysis (CFA)",
                "model_fit_indices": native_fit_measures
            }
            
            json_data = json.dumps(data_to_export, indent=4)

            self.main.clipboard_clear()
            self.main.clipboard_append(json_data)
            
            messagebox.showinfo("Copied", "CFA results have been copied to the clipboard in JSON format.")

        except Exception as e:
            messagebox.showerror("Copy Error", f"An error occurred while copying data: {e}")

    def start_qwen_analysis(self):
        if not self.fit_measures:
            messagebox.showwarning("No Data", "There is no analysis data to analyze. Please run the analysis first.")
            return

        def analysis_task(progress_dialog):
            progress_dialog.update_message("Building analysis prompt...")
            prompt = self._build_cfa_analysis_prompt()
            
            progress_dialog.update_message("Calling AI API for analysis...")
            analysis_result = llm.invoke(prompt)
            return analysis_result.content

        def on_success(result):
            if result:
                show_analysis_window(self.main, result)

        def on_error(error):
            messagebox.showerror("AI Analysis Error", f"An error occurred during AI analysis: {error}")

        self.progress_manager.run_with_progress(
            analysis_task,
            title="AI Result Analysis",
            message="Connecting to AI...",
            success_callback=on_success,
            error_callback=on_error
        )

    def _build_cfa_analysis_prompt(self):
        fit_indices_str = "\n".join([f"- {index}: {value:.4f}" for index, value in self.fit_measures.items()])

        return f"""
You are an expert psychologist and statistician specializing in psychometrics. Your task is to interpret the results of a Confirmatory Factor Analysis (CFA).

**Background Information:**
- **Goal**: The analysis was performed to test how well the observed data from a personality questionnaire fits the theoretical "Big Five" personality model. This model posits five distinct factors: Neuroticism, Extraversion, Openness, Agreeableness, and Conscientiousness.
- **Analysis Performed**: Confirmatory Factor Analysis (CFA).

**Model Fit Indices:**
Here are the results from the analysis:
{fit_indices_str}

**Your Task:**
1.  **Interpret Each Index**: For each of the fit indices provided (CFI, TLI, RMSEA, SRMR, etc.), explain what it measures and compare its value to the commonly accepted thresholds for good model fit.
    -   CFI/TLI: Good fit is typically > 0.90, ideal is > 0.95.
    -   RMSEA: Good fit is typically < 0.08, ideal is < 0.06.
    -   SRMR: Good fit is typically < 0.08.
2.  **Synthesize and Conclude**:
    -   Based on the combination of all indices, what is your overall conclusion about the model fit?
    -   Does the data strongly support the "Big Five" structure? Or are there signs of poor fit?
    -   Provide a clear, final judgment in plain language. For example: "The model shows an excellent fit to the data," or "The model fit is poor, suggesting the data does not align well with the theoretical structure."

Please provide your analysis in a clear, structured format.
"""

    def set_language(self, lang):
        self.current_lang = lang
        self.collapsible_help.update_content(
            title=HELP_CONTENT[lang]['title'],
            content=HELP_CONTENT[lang]['content']
        )
        self.select_button.config(text=UI_TEXT[lang]['select_db'])
        self.run_button.config(text=UI_TEXT[lang]['run_analysis'])
        self.results_tree.master.config(text=UI_TEXT[lang]['analysis_results'])
        
    def setData(self, appKey, updateTree):
        pass